package com.xin.di.uav.common.annotate;

import com.xin.di.uav.common.awt.JwtUtil;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@RequiredArgsConstructor
public class DistributedLockAspect {
    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate redisTemplate;

    @Around("@annotation(lock)")
    public Object around(ProceedingJoinPoint joinPoint, DistributedLock lock) throws Throwable {
        boolean userDimension = lock.userDimension();
        String lockKey;
        if (userDimension) {
            lockKey = generateLockKey(joinPoint, lock.key()) + JwtUtil.getUserId();
        } else {
            lockKey = generateLockKey(joinPoint, lock.key());
        }

        boolean locked = false;
        try {
            locked = tryLock(lockKey, lock.expireTime(), lock.waitTime());
            if (!locked) {
                throw new RuntimeException("获取分布式锁失败");
            }
            return joinPoint.proceed();
        } finally {
            if (locked) {
                redisTemplate.delete(lockKey);
            }
        }
    }

    private boolean tryLock(String key, long expireTime, long waitTime) {
        long end = System.currentTimeMillis() + waitTime * 1000;
        while (System.currentTimeMillis() < end) {
            Boolean success = redisTemplate.opsForValue().setIfAbsent(key, "1", expireTime, TimeUnit.SECONDS);
            if (Boolean.TRUE.equals(success)) {
                return true;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        return false;
    }

    private String generateLockKey(ProceedingJoinPoint joinPoint, String customKey) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        return "lock:" + customKey + ":" + Arrays.toString(joinPoint.getArgs());
    }
}
